-- ======================================================================
-- Hell Logic Sniffer Addon
--
-- Copyright 2013 Hell-Prototypes
--
-- http://www.hellprototypes.com/
-- ======================================================================

ModuleClass = "LS_DECODER"
ModuleName  = "CUSTOM"
ModuleAlias = "CAN Bus"
ModuleParam1= "CH Number"
ModuleParam2= "Bit Rate"
ModuleTip   = [[
CAN Bus Decoder use CUSTOM addon interface

FIXME AND HELP: Only test with "Standard Frame". Not test with "Extend Frame"
]]

g_bit_rate = 0
g_sampling_period = 0
g_NBT_bit_len = 0

g_ch_negedge_table = {}
g_ch_negedge_table_index = 1
g_channel_bit_index = 0

g_bit_stuffing_count = 0
g_last_bit_level = 0

function main ()
	LS_API.DebugMsg("= CAN Bus Decoder addon use CUSTOM addon interface =")
    -- param check
    if type(LS_PARAM) ~= "table" then
    	LS_API.MessageBox("<CAN> LS_PARAM was not a table class")
		return
    end
    if #LS_PARAM ~= 6 then
    	LS_API.MessageBox("<CAN> LS_PARAM's size must = 6, size = " .. #LS_PARAM)
    end


    g_ch_num = LS_PARAM[1]
    g_bit_rate = LS_PARAM[2]
    local common_param = LS_API.GetCommonParameter()
	LS_API.DebugMsg("common_param len = ".. #common_param)
	if #common_param > 0 then
		g_sampling_period = common_param[1]
	end

	--get channel data
	g_ch_negedge_table = LS_API.GetChannelNegedgeTable(g_ch_num)
	if type(g_ch_negedge_table) ~= "table" then
    	LS_API.MessageBox("<CAN>Read channel data fail...")
		return
    end

    if #g_ch_negedge_table == 0 then
    	LS_API.MessageBox("<CAN>No data in selected channel : " .. g_ch_num)
		return
    end
    LS_API.DebugMsg("g_ch_negedge_table size = ".. #g_ch_negedge_table)
    LS_API.DebugMsg("last package start bit at = ".. g_ch_negedge_table[#g_ch_negedge_table - 1])

	g_NBT_bit_len = math.floor((1000000000/g_bit_rate)/g_sampling_period)
	LS_API.DebugMsg("g_NBT_bit_len = ".. g_NBT_bit_len)

	while g_ch_negedge_table_index < #g_ch_negedge_table do
		--find first valid byte and offset half bit
		g_channel_bit_index = find_next_failling_edge(g_channel_bit_index) + math.floor(g_NBT_bit_len/2)
		if g_ch_negedge_table_index > #g_ch_negedge_table then
			-- no more Data
			return
		end

		local loop = false
		repeat
			-- Start of fram bit
			local point_list = {}
			local value, point_list = read_bits_value(1)
			if value < 0 then
				break
			end
			if not push_data_to_host("SOF", point_list) then
				return
			end

			-- 11 bit IDENTIFIER
			value, point_list = read_bits_value(11)
			if value < 0 then
                LS_API.DebugMsg("value = ".. value)
				break
			end
			if not push_data_to_host(value, point_list) then
				return
			end


			-- RTR OR SRR
			value, point_list = read_bits_value(1)
			if value < 0 then
				break
			end
			if not push_data_to_host("R/S", point_list) then
				return
			end

			--IDE flag
			local ide_flag = -1
			ide_flag, point_list = read_bits_value(1)
			if ide_flag < 0 then
				break
			end
			if not push_data_to_host("IDE", point_list) then
				return
			end

			-- Extend fram
			if ide_flag == true then
				value, point_list = read_bits_value(18)
				if value < 0 then
					break
				end
				if not push_data_to_host(value, point_list) then
					return
				end

				--RTR
				value, point_list = read_bits_value(1)
				if value < 0 then
					break
				end
				if not push_data_to_host("RTR", point_list) then
					return
				end

				-- RB1
				value, point_list = read_bits_value(1)
				if value < 0 then
					break
				end
				if not push_data_to_host("RB1", point_list) then
					return
				end
			end

			-- RB0
			value, point_list = read_bits_value(1)
			if value < 0 then
				break
			end
			if not push_data_to_host("RB0", point_list) then
				return
			end

			-- DLC
			local data_field_len = -1
			data_field_len, point_list = read_bits_value(4)
			if data_field_len < 0 then
				break
			end
			if not push_data_to_host(data_field_len, point_list) then
				return
			end

			-- Data field
			local read_fail = false
			for i=1, data_field_len do
				value, point_list = read_bits_value(8)
				if value < 0 then
					read_fail = true
					break
				end
				if not push_data_to_host(value, point_list) then
					return
				end
			end
			if read_fail == true then
				break
			end

			-- CRC
			value, point_list = read_bits_value(15)
			if value < 0 then
				break
			end
			if not push_data_to_host(value, point_list) then
				return
			end

			--CRC Delimiter
			value, point_list = read_bits_value(1)
			if value < 0 then
				break
			end

			-- ACK
			value, point_list = read_bits_value(1)
			if value < 0 then
				break
			end
			if not push_data_to_host("ACK", point_list) then
				return
			end
		until loop == false
	end
end
-- -----------------------------------------------------------------------------
function push_data_to_host(value, points)
	local decode_out = {}
	if type(points) ~= "table" then
		return false
	end

	decode_out[1] = points[1]
	decode_out[2] = value
	if type(points) == "table" then
		for i=1, #points do
			decode_out[i+2] = points[i]
		end
	end
	local ret = LS_API.DecodeElementPushBack(decode_out)
	if ret ~= 0 then
		LS_API.MessageBox("<CAN> Fail to push decode result to host... ret =" .. ret)
		return false
	end

	return true
end
-- -----------------------------------------------------------------------------
function read_bits_value(read_len)
	local point_list = {}
	local point_list_index = 1
	if read_len > 32 then
		return -1, point_list
	end

	local ret_data = 0
	local bit_valid = 1

	repeat
		local level = get_bit_level(g_channel_bit_index)
		bit_valid = 1
		if g_last_bit_level == level then
			g_bit_stuffing_count = g_bit_stuffing_count + 1
			if g_bit_stuffing_count > 5 then
				point_list = {}
				return -2, point_list
			end
		else
			if g_bit_stuffing_count == 5 then -- stuffing bit
				bit_valid = 0
			end
			g_bit_stuffing_count = 1
		end
		g_last_bit_level = level

		if bit_valid == 1 then
			point_list[point_list_index] = g_channel_bit_index
			point_list_index = point_list_index + 1
			read_len = read_len - 1
			if level then
				ret_data = ret_data | (1 << read_len) --MSB First
			end
		end
		g_channel_bit_index = g_channel_bit_index + g_NBT_bit_len

	until read_len == 0

	return ret_data, point_list
end
-- -----------------------------------------------------------------------------
function get_bit_level(bit_index)
    local level = LS_API.GetChannelLevelByIndex(g_ch_num, bit_index);
	if level == 1 then
		return true
	else
		return false
    end
end
-- -----------------------------------------------------------------------------
function find_next_failling_edge(base)
	local index
    while g_ch_negedge_table_index <= #g_ch_negedge_table do
    	index = g_ch_negedge_table[g_ch_negedge_table_index]
    	if index >= base then
            g_last_bit_level = 1
			g_bit_stuffing_count = 1
    		return index
    	end
    	g_ch_negedge_table_index = g_ch_negedge_table_index + 1
    end
	return 0
end
